<?php

class user extends data
{
   protected $main_table      = 'meccano_users';
   protected $translate_table = TRUE;
   
   private $_roles            = NULL;
   private $_groups           = NULL;
   private $types             = array('internal','facebook','twitter','google','vkontakte');
   
   public  $key               = '';
   public  $login             = NULL;
   public  $type              = 'internal';
   public  $lastloged         = NULL;
   public  $groupsByString    = '';

   public function  __construct($id = NULL)
   {
      parent::__construct();

      $this->loadEmpty();
      if(!empty($id))
      {
         return $this->load($id);
      }

      return NULL;
   }


   /**
    * Create object for empty user
    * 
    * @param array $fields
    * @return object 
    */
   protected function loadEmpty($fields = NULL)
   {
      $FIELDS = fields::getInstance();
      $fields = $FIELDS->splitFields(array('belong'=>'u', 'active'=>TRUE));
      return parent::loadEmpty($fields);
   }


   /**
    * Load user by ID
    * 
    * @param integer $id
    * @param integer $lang_id
    * @return boolean 
    */
   public function load($id, $lang_id = NULL)
   {
      if(empty($id)) return FALSE;

      if(is_numeric($id)) $placeholder = '`t1`.`id` = "%i"';
      else $placeholder = '`t1`.`login` = "%s"';

      $DB = database::getInstance();

      $DB->DB_QueryBuilder('SELECT `t1`.*
         FROM `'. $this->main_table .'` AS `t1`
         WHERE `t1`.`deleted` IS NULL
         AND '. $placeholder, array($id));

      $data = $DB->DB_Fetch();

      $data = !empty($data) ? reset($data) : NULL;
      return $this->loadFromArray($data);
   }
   
   
   /**
    * Load User by temporary Key
    * 
    * @param string $key
    * @return object 
    */
   public function loadByKey($key)
   {
      $FILTER = new filters();
      if(empty($key) || !$FILTER->isValidAlphaNum($key)) return FALSE;
      
      $DB = database::getInstance();
      $DB->DB_QueryBuilder('SELECT `t1`.*
         FROM `'. $this->main_table .'` AS `t1`
         WHERE `t1`.`deleted` IS NULL
         AND `t1`.`vdate` > NOW()
         AND `t1`.`key` = "%s"', array($key));
      
      $data = $DB->DB_Fetch();
      
      $data = !empty($data) ? reset($data) : NULL;
      return $this->loadFromArray($data);
   }
   
   
   /**
    * Load User by Email
    * 
    * @param string $email
    * @return object 
    */
   public function loadByEmail($email)
   {
      if(empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) return FALSE;
      
      $DB = database::getInstance();
      $DB->DB_QueryBuilder('SELECT `t1`.*
         FROM `'. $this->main_table .'` AS `t1`
         WHERE `t1`.`deleted` IS NULL
         AND `t1`.`email` = "%s"', array($email));
      
      $data = $DB->DB_Fetch();
      
      $data = !empty($data) ? reset($data) : NULL;
      return $this->loadFromArray($data);
   }
   
   
   public function loadByLogin($login)
   {
      $FILTER = new filters();
      if(empty($login) || !$FILTER->isValidAlphaNum($login)) return FALSE;
      
      $DB = database::getInstance();
      $DB->DB_QueryBuilder('SELECT `t1`.*
         FROM `'. $this->main_table .'` AS `t1`
         WHERE `t1`.`deleted` IS NULL
         AND `t1`.`vdate` > NOW()
         AND `t1`.`login` = "%s"', array($login));
      
      $data = $DB->DB_Fetch();
      
      $data = !empty($data) ? reset($data) : NULL;
      return $this->loadFromArray($data);
   }
   
   /**
    * Load user by authorization credentials
    * 
    * @param string $username
    * @param string $password '
    * @return object
    */
   public function authorize($login, $password)
   {
      $DB     = database::getInstance();
      $FILTER = new filters();

      if (!$FILTER->isValidAlphaNum($login))
      {
         $login = FALSE;
         error::getInstance()->setError(array('code' => 'login', 'varkey' => $this->langvar_login_chars), 4);
      }

      $password = $this->encryptPassword($password);

      if ($login !== FALSE && $password != FALSE)
      {
         // TODO: переделать метод автологина
         $DB->DB_QueryBuilder('SELECT * 
            FROM `meccano_users` 
            WHERE `type` = "internal" AND `login` = "%s" AND `pwd` = "%s" LIMIT 0, 1', array($login, $password));
         $DB->DB_FetchFlat();

         if(!empty($DB->fetched))
         {
            $this->loadFromArray($DB->fetched);
            $this->getRoles();
            $this->getGroups();

            $DB->sql = 'UPDATE `meccano_users` SET `lastloged` = NOW() WHERE `id` = "' . $this->id . '"';
            $DB->DB_Query();
            
            return TRUE;

         } else
         {
            error::getInstance()->setError(array('code' => 'login', 'varkey' => 'loginfailed'), 4);
         }
         
         return FALSE;
      }
   }


   public function loadFromArray($data)
   {
      if(!empty($data) && !empty($data['id']))
      {
         $this->id        = $data['id'];
         $this->login     = $data['login'];
         $this->type      = $data['type'];
         $this->lastloged = !empty($data['lastloged']) ? mysql2time($data['lastloged']) : time();
         $this->key       = $data['key'];
         parent::load($data);

         return TRUE;
      }

      return FALSE;
   }

   public function getRoles()
   {
      if($this->_roles === NULL && !empty($this->id))
      {
         $DB      = database::getInstance();
         if($this->id == 1)
         {
            $DB->sql = 'SELECT * FROM `meccano_roles`';
         } else
         {
            $DB->sql = 'SELECT DISTINCT `tr`.*
               FROM `meccano_roles` AS `tr`, `meccano_user2group` AS `tg`, `meccano_role2group` AS `rg`
               WHERE `tg`.`uid` = '. $this->id .'
               AND `tg`.`gid` = `rg`.`gid`
               AND `rg`.`rid` = `tr`.`id`
               ORDER BY `role`';
         }
         $DB->DB_Fetch();

         $roles = array();
         for ($i = 0; $i < $DB->rows; $i++)
         {
            $roles[$DB->fetched[$i]['id']] = $DB->fetched[$i]['role'];
         }

         $this->_roles = $roles;
      }

      return $this->_roles;
   }

   
   /**
    * Set User's Groups
    * 
    * @param array $groups id of the groups
    * @return boolean 
    */
   public function setGroups($groups)
   {
      if(empty($this->id)) return FALSE;
      
      $DB = database::getInstance();
      $DB->sql = 'SELECT `id` FROM `meccano_groups`';
      $groups_exist = $DB->DB_GetFlat();

      $groups = array_intersect((array)$groups, (array)$groups_exist);
      $groups = array_values($groups);

      $DB->sql = 'DELETE FROM `meccano_user2group` WHERE `uid` = ' . $this->id;
      $DB->DB_Query();

      if(!empty($groups))
      {
         $sql_vars = array();
         foreach ($groups AS $gid)
         {
            $sql_vars[] = '(' . $this->id . ', ' . $gid . ')';
         }

         if (!empty($sql_vars))
         {
            $DB->sql = 'INSERT INTO `meccano_user2group` (`uid`, `gid`) VALUES ' . implode(', ', $sql_vars);
            $DB->DB_Query();
         }
      }

      $this->_groups = NULL;
   }

   public function getGroups()
   {
      if($this->_groups === NULL && !empty($this->id))
      {
         $this->_groups = FALSE;

         $DB = database::getInstance();
         $DB->sql = 'SELECT DISTINCT `gr`.*
            FROM `meccano_groups` AS `gr`, `meccano_user2group` AS `ug`
            WHERE `gr`.`id` = `ug`.`gid`
            AND `ug`.`uid` = '. $this->id;
         $DB->DB_Fetch();
         for($i=0, $t=$DB->rows; $i<$t; $i++)
         {
            $this->_groups[$DB->fetched[$i]['id']] = $DB->fetched[$i];
         }
      }

      return $this->_groups;
   }

   public function isInGroup($id)
   {
      $groups = $this->getGroups();
      if(is_numeric($id) && isset($groups[$id]))
      {
         return TRUE;
      } elseif(is_array($groups))
      {
         foreach($groups AS $g)
         {
            if($g['group'] == $id)
            {
               return TRUE;
            }
         }
      }

      return FALSE;
   }

   /**
    * Check if User has given role 
    * 
    * @param string $role
    * @return boolean 
    */
   public function hasRole($role)
   {
      $roles = $this->getRoles();
      if(is_array($roles) && in_array($role, $roles))
      {
         return TRUE;
      }

      return FALSE;
   }

   public function getFields($alias = null, $tab = null)
   {
      $node   = 0;
      $filter = array('belong'=>'u', 'active'=>TRUE);
      return parent::getFields($node, $filter);
   }

   /**
    * Change User password, by checking old one
    *
    * @param mixed  $old - Old password, if given TRUE - old password will not be checked
    * @param string $new - New password
    * @return bool
    */
   
   public function changePassword($old, $new)
   {
      if (!empty($old) && !empty($new))
      {
         $DB = database::getInstance();
         $sql = 'UPDATE `'. $this->main_table .'` SET `pwd` = "' . $this->encryptPassword($new) . '" WHERE `id` = ' . $this->id;
         if ($old !== TRUE)
            $sql .= ' AND `pwd` = "'. $this->encryptPassword($old) . '"';
         $DB->sql = $sql;
         $DB->DB_Query();
         if($DB->DB_GetAffected())
            return TRUE;
      }
      return FALSE;
   }

   public function save($data)
   {  
      $FILTER = new filters();
      if(empty($this->id) && (empty($data['login']) || !(filter_var($data['login'], FILTER_VALIDATE_EMAIL) || $FILTER->isValidAlphaNum($data['login']))))
      {
         return FALSE;
      }
      
      # Set Temporary KEY
      if(isset($data['key']))
      {
         $this->key = empty($data['key']) ? '' : md5($data['key']);
         unset($data['key']);
      }

      $sql = parent::save($data);
      if(empty($sql['main'])) return FALSE;
      $sql['main'][] = '`key` = "'. $this->key .'"';
      
      
      $DB       = database::getInstance();
      $sql_main = '';
      if(empty($this->id))
      {
         $sql['main'][] = '`login` = "'. $data['login'] .'"';
         $sql['main'][] = '`type` = "'. (in_array($this->type, $this->types) ? $this->type : 'internal') .'"';
         $sql_main = 'INSERT INTO `'. $this->main_table .'` SET'. implode(', ', $sql['main']);
      } else
      {
         $sql_main = 'UPDATE `'. $this->main_table .'` SET'. implode(', ', $sql['main']) .' WHERE `id` = '. $this->id;
      }

      $DB->DB_StartTransaction();
      $DB->sql = $sql_main;
      $DB->DB_Query();
      if($DB->DB_EndTransaction())
      {
         # UPDATE ID
         if(empty($this->id))
         {
            $this->id = $DB->DB_LastID();
         }

         return TRUE;
      }

      return FALSE;
   }
   
   
   /**
    * Encrypt Password
    * 
    * @param string $password
    * @return string 
    */
   private function encryptPassword($password)
   {
      // TODO: replace with strongest encryption
      return md5($password);
   }
}